
package com.hlkj.pay.app.finance.impl;

import cn.hutool.extra.servlet.ServletUtil;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.pojo.LocalContext;
import com.hlkj.framework.common.pojo.LocalRequest;
import com.hlkj.framework.common.util.collection.CollectionUtils;
import com.hlkj.framework.common.util.id.IdUtils;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.pay.app.finance.IFinanceRecordAppService;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.openapi.IOpenApiAppService;
import com.hlkj.pay.app.order.IPayOrderAppService;
import com.hlkj.pay.app.order.impl.OrderHandler;
import com.hlkj.pay.common.*;
import com.hlkj.pay.dto.LocalAdminUserRequest;
import com.hlkj.pay.dto.LocalMerchantRequest;
import com.hlkj.pay.dto.common.DefaultConfig;
import com.hlkj.pay.dto.finance.MerchantFinanceRecordPageQueryDto;
import com.hlkj.pay.enums.*;
import com.hlkj.pay.infrastructure.model.finance.MerchantAmountDO;
import com.hlkj.pay.infrastructure.model.finance.MerchantFinanceChargeDO;
import com.hlkj.pay.infrastructure.model.finance.MerchantFinanceRecordDO;
import com.hlkj.pay.infrastructure.model.merchant.MerchantInfoDO;
import com.hlkj.pay.infrastructure.model.pay.PayWayDO;
import com.hlkj.pay.service.common.IOperatedLogService;
import com.hlkj.pay.service.common.IPlatFormConfigService;
import com.hlkj.pay.service.finance.IMerchantFinanceChargeService;
import com.hlkj.pay.service.finance.IMerchantFinanceRecordService;
import com.hlkj.pay.service.merchant.IMerchantService;
import com.hlkj.pay.service.pay.IPayWayService;
import com.hlkj.pay.util.MerchantUtils;
import com.hlkj.pay.util.OrderCodeUtil;
import com.hlkj.pay.vo.finance.req.MerchantFinanceRecordCommonReq;
import com.hlkj.pay.vo.finance.req.MerchantFinanceRecordReq;
import com.hlkj.pay.vo.finance.req.MerchantFinanceRecordReturnReq;
import com.hlkj.pay.vo.merchant.req.MerchantChargeReq;
import com.hlkj.pay.vo.openapi.order.resp.PayOrderSubmitApiResp;
import com.hlkj.pay.vo.order.OpenApiResult;
import com.hlkj.pay.vo.order.req.PayOrderParamReq;
import com.hlkj.pay.vo.order.resp.PayOrderResp;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.List;

/**
 * @author HlpayTeam
 * @date 2024/11/14 15:33
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FinanceRecordAppServiceImpl implements IFinanceRecordAppService {

    private final IMerchantFinanceRecordService merchantFinanceRecordService;

    private final IMerchantService merchantService;

    private final IOperatedLogService merchantLogService;

    private final IMerchantFinanceChargeService merchantFinanceChargeService;

    private final IPayOrderAppService payOrderAppService;

    private final IPlatFormConfigService platFormConfigService;

    private final IOpenApiAppService openApiAppService;

    private final OrderHandler orderHandler;

    private final OrderCodeUtil orderCodeUtil;

    private final IPayWayService payWayService;

    @Value("${hlkj.domain}")
    private String domain;

    @Value("${hlkj.mer-domain.prod}")
    private String merDomain;

    @Override
    public CommonResult<PayOrderSubmitApiResp> merchantCharge(MerchantChargeReq merchantChargeReq) {
        log.info("merchantCharge merchantChargeReq:{}", JsonUtils.toJsonString(merchantChargeReq));
        // 获取当前登录人信息
        LocalAdminUserRequest localAdminUserRequest = (LocalAdminUserRequest) LocalContext.get();
        try {
            DefaultConfig defaultConfig = platFormConfigService.getDefaultConfig();
            if (defaultConfig == null || defaultConfig.getRechargeConfig() == null) {
                return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_CONFIG_NOT_EXIST);
            }
            DefaultConfig.RechargeConfig rechargeConfig = defaultConfig.getRechargeConfig();
            if (!CommonEnum.YES_NO_TYPE.YES.getCode().equals(rechargeConfig.getStatus())) {
                return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_CONFIG_NOT_EFFECT);
            }
            BigDecimal amount = new BigDecimal(merchantChargeReq.getAmount());

            if (rechargeConfig.getMaxMoney() != null && rechargeConfig.getMaxMoney().compareTo(amount) < 0) {
                return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_AMOUNT_EFFECT);
            }
            if (rechargeConfig.getMinMoney() != null && rechargeConfig.getMinMoney().compareTo(amount) > 0) {
                return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_AMOUNT_EFFECT);
            }
            DefaultConfig.RechargeConfig.PayType rechargeConfigPayType = rechargeConfig.getPayType();
            // PC端:1 WAP端:2 小程序:3 APP端:4
            switch (merchantChargeReq.getChargeType()) {
                case 2:
                    if (CollectionUtils.isEmpty(rechargeConfigPayType.getMobile())) {
                        return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_CONFIG_NOT_EFFECT);
                    }
                    if (!rechargeConfigPayType.getMobile().contains(merchantChargeReq.getPayWayCode())) {
                        return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_WAY_EFFECT);
                    }
                    break;
                case 3:
                    if (CollectionUtils.isEmpty(rechargeConfigPayType.getMini())) {
                        return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_CONFIG_NOT_EFFECT);
                    }
                    if (!rechargeConfigPayType.getMini().contains(merchantChargeReq.getPayWayCode())) {
                        return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_WAY_EFFECT);
                    }
                    break;
                case 4:
                    if (CollectionUtils.isEmpty(rechargeConfigPayType.getApp())) {
                        return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_CONFIG_NOT_EFFECT);
                    }
                    if (!rechargeConfigPayType.getApp().contains(merchantChargeReq.getPayWayCode())) {
                        return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_WAY_EFFECT);
                    }
                    break;
                default:
                    if (CollectionUtils.isEmpty(rechargeConfigPayType.getPc())) {
                        return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_CONFIG_NOT_EFFECT);
                    }
                    if (!rechargeConfigPayType.getPc().contains(merchantChargeReq.getPayWayCode())) {
                        return CommonResult.error(FinanceResultCode.FINANCE_RECHARGE_WAY_EFFECT);
                    }
            }

            PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByType(merchantChargeReq.getPayWayCode());
            if (payType == null) {
                log.info("提交的支付类型错误:{}", merchantChargeReq.getPayWayCode());
                return CommonResult.error(OpenApiResultCode.PAY_WAY_NOT_EXITS);
            }
            PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(merchantChargeReq.getPaySubType());;

            // 获取支付方式
            PayWayDO payWayDO = payWayService.queryByType(payType.getCode(), scenePayType, merchantChargeReq.getSceneType());
            if (payWayDO == null) {
                return CommonResult.error(PayResultCode.PAY_WAY_NOT_EXITS);
            }
            CommonResult<Void> voidCommonResult = openApiAppService.setAppInfoOnly(rechargeConfig.getAppId());
            if (!voidCommonResult.isSuccess()) {
                return CommonResult.error(voidCommonResult.getCode(), voidCommonResult.getMsg());
            }
            LocalMerchantRequest localMerchantRequest = (LocalMerchantRequest) LocalContext.get();
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            PayOrderParamReq payOrderParamReq = new PayOrderParamReq();
            payOrderParamReq.setPayWayCode(payWayDO.getCode());
            payOrderParamReq.setSceneType(payWayDO.getSceneType());
            payOrderParamReq.setPaySubType(payWayDO.getSubType());
            payOrderParamReq.setPayType(PayEnums.PAY_TYPE.fromByCode(payWayDO.getType()).getType());
            payOrderParamReq.setMchOrderNo(orderCodeUtil.orderNo(PayOrderEnums.ORDER_NO_TYPE.RECHARGE_APPLY));
            payOrderParamReq.setAmount(merchantChargeReq.getAmount());
            payOrderParamReq.setClientIp(ServletUtil.getClientIP(request, null));
            payOrderParamReq.setSubject("商户充值");
            payOrderParamReq.setDescription("商户充值");
            payOrderParamReq.setOrderType(PayOrderEnums.PAY_ORDER_TYPE.RECHARGE);
            payOrderParamReq.setRelationOrderNo(localMerchantRequest.getSn());
            // payOrderParamReq.setExtra();
            payOrderParamReq.setNotifyUrl(domain + MerchantExtConstant.MERCHANT_RECHARGE_ASYNC_CALLBACK_URL);
            payOrderParamReq.setRedirectUrl(merDomain);
            CommonResult<PayOrderSubmitApiResp> respCommonResult = payOrderAppService.submitOrder(payOrderParamReq);
            // 从新设置登录人信息
            LocalContext.set(localAdminUserRequest);
            if (respCommonResult.isSuccess()) {
                PayOrderSubmitApiResp respCommonResultData = respCommonResult.getData();
                MerchantFinanceChargeDO merchantFinanceChargeDO = new MerchantFinanceChargeDO();
                merchantFinanceChargeDO.setMchOrderNo(respCommonResultData.getMchOrderNo());
                merchantFinanceChargeDO.setPayOrderNo(respCommonResultData.getPayOrderNo());
                merchantFinanceChargeDO.setSn(localAdminUserRequest.getSn());
                merchantFinanceChargeDO.setSysType(localAdminUserRequest.getSysType());
                merchantFinanceChargeDO.setAmount(amount);
                merchantFinanceChargeDO.setState(respCommonResultData.getState());
                merchantFinanceChargeDO.setChargeType(merchantChargeReq.getChargeType());
                merchantFinanceChargeDO.setPayWayCode(payWayDO.getCode());
                merchantFinanceChargeService.addFinanceCharge(merchantFinanceChargeDO);
            }
            return respCommonResult;
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("merchantCharge error:{}", e, e.getMessage());
            return CommonResult.error(CommonResultCode.SERVICE_ERROR);
        }
        finally {
            // 从新设置登录人信息
            LocalContext.set(localAdminUserRequest);
        }
    }

    @Override
    public CommonResult<Void> addFinanceRecord(MerchantFinanceRecordCommonReq financeRecordCommonReq) {
        log.info("addFinanceRecord financeRecordCommonReq:{}", JsonUtils.toJsonString(financeRecordCommonReq));
        try {
            MerchantInfoDO merchantInfoDO = merchantService.queryMerchant(financeRecordCommonReq.getSn());
            if (merchantInfoDO == null) {
                return CommonResult.error(MerchantResultCode.MERCHANT_NOT_EXIST);
            }
            MerchantAmountDO merchantAmountDO = merchantService.queryMerchantAmount(merchantInfoDO.getSn());
            MerchantFinanceRecordDO merchantFinanceRecordDO = new MerchantFinanceRecordDO();
            String amount = financeRecordCommonReq.getAmount();
            merchantFinanceRecordDO.setAmount(new BigDecimal(amount));
            boolean deductionAmount = true;
            String orderNo = null;
            if (financeRecordCommonReq instanceof MerchantFinanceRecordReq) {
                MerchantFinanceRecordReq merchantFinanceRecordReq = (MerchantFinanceRecordReq) financeRecordCommonReq;
                orderNo = merchantFinanceRecordReq.getOrderNo();
                deductionAmount = merchantFinanceRecordReq.isDeductionAmount();
            }
            if (merchantAmountDO == null) {
                merchantFinanceRecordDO.setBeforeAmount(new BigDecimal("0"));
                merchantFinanceRecordDO.setAfterAmount(new BigDecimal(amount));
                merchantAmountDO = new MerchantAmountDO();
                merchantAmountDO.setSn(merchantInfoDO.getSn());
                merchantAmountDO.setMerchBalance(new BigDecimal(0));
                merchantAmountDO.setFrozenAmount(new BigDecimal(0));
                merchantAmountDO.setMarketAccount(new BigDecimal(0));
                merchantAmountDO.setFeeAmount(new BigDecimal(0));
                merchantAmountDO.setProfitAmount(new BigDecimal(0));
                merchantAmountDO.setTransitAmount(new BigDecimal(0));
                merchantAmountDO.setDepositAmount(new BigDecimal(0));
                merchantAmountDO.setSnType(merchantInfoDO.getSnType());
            }
            BigDecimal bigDecimal = new BigDecimal(amount);
            if (CommonEnum.YES_NO_TYPE.NO.getCode().equals(financeRecordCommonReq.getChangeType())) {
                bigDecimal = bigDecimal.multiply(new BigDecimal(-1));
            }
            String remark = "";
            FinanceEnum.FINANCE_TYPE financeType = FinanceEnum.FINANCE_TYPE.from(financeRecordCommonReq.getType());
            String operType = financeType.getDesc();
            if (deductionAmount) {
                switch (financeType) {
                    case AGENT_AMOUNT:
                        merchantFinanceRecordDO.setBeforeAmount(merchantAmountDO.getMerchBalance());
                        merchantAmountDO.setMerchBalance(merchantAmountDO.getMerchBalance().add(bigDecimal));
                        merchantFinanceRecordDO.setAfterAmount(merchantAmountDO.getMerchBalance());
                        break;
                    case MERCHANT_FEE:
                        merchantFinanceRecordDO.setBeforeAmount(merchantAmountDO.getFeeAmount());
                        merchantAmountDO.setFeeAmount(merchantAmountDO.getFeeAmount().add(bigDecimal));
                        merchantFinanceRecordDO.setAfterAmount(merchantAmountDO.getFeeAmount());
                        remark = financeType.getDesc() + (CommonEnum.YES_NO_TYPE.NO.getCode().equals(financeRecordCommonReq.getChangeType()) ? "减少" : "增加") + amount;
                        break;
                    case MERCHANT_DEPOSIT:
                        merchantFinanceRecordDO.setBeforeAmount(merchantAmountDO.getDepositAmount());
                        merchantAmountDO.setDepositAmount(merchantAmountDO.getDepositAmount().add(bigDecimal));
                        merchantFinanceRecordDO.setAfterAmount(merchantAmountDO.getDepositAmount());
                        remark = financeType.getDesc() + (CommonEnum.YES_NO_TYPE.NO.getCode().equals(financeRecordCommonReq.getChangeType()) ? "减少" : "增加") + amount;
                        break;
                    case MERCHANT_MARKET:
                        merchantFinanceRecordDO.setBeforeAmount(merchantAmountDO.getMarketAccount());
                        merchantAmountDO.setMarketAccount(merchantAmountDO.getMarketAccount().add(bigDecimal));
                        merchantFinanceRecordDO.setAfterAmount(merchantAmountDO.getMarketAccount());
                        remark = financeType.getDesc() + (CommonEnum.YES_NO_TYPE.NO.getCode().equals(financeRecordCommonReq.getChangeType()) ? "减少" : "增加") + amount;
                        break;
                    case AGENT_TRANSIT:
                        merchantFinanceRecordDO.setBeforeAmount(merchantAmountDO.getTransitAmount());
                        merchantAmountDO.setTransitAmount(merchantAmountDO.getTransitAmount().add(bigDecimal));
                        merchantFinanceRecordDO.setAfterAmount(merchantAmountDO.getTransitAmount());
                        break;
                    case AGENT_FROZEN:
                        merchantFinanceRecordDO.setBeforeAmount(merchantAmountDO.getFrozenAmount());
                        merchantAmountDO.setFrozenAmount(merchantAmountDO.getFrozenAmount().add(bigDecimal));
                        merchantFinanceRecordDO.setAfterAmount(merchantAmountDO.getFrozenAmount());
                        break;
                    case AGENT_PROFIT:
                        merchantFinanceRecordDO.setBeforeAmount(merchantAmountDO.getProfitAmount());
                        merchantAmountDO.setProfitAmount(merchantAmountDO.getProfitAmount().add(bigDecimal));
                        merchantFinanceRecordDO.setAfterAmount(merchantAmountDO.getProfitAmount());
                        break;
                }
                if (merchantAmountDO.getId() == null) {
                    merchantService.addMerchantAmount(merchantAmountDO);
                }
                else {
                    merchantService.updateMerchantAmount(merchantAmountDO);
                }
            }
            merchantFinanceRecordDO.setFinanceId(IdUtils.getSnowflakeNextIdStr());
            merchantFinanceRecordDO.setSn(financeRecordCommonReq.getSn());
            merchantFinanceRecordDO.setRemark(financeRecordCommonReq.getRemark());
            MerchantEnum.MERCHANT_SN_TYPE snType = MerchantEnum.MERCHANT_SN_TYPE.from(merchantInfoDO.getSnType());
            switch (snType) {
                case MERCHANT:
                    merchantFinanceRecordDO.setSysType(CommonEnum.SYS_TYPE.MERCHANT.getCode());
                    if (StringUtils.hasText(remark)) {
                        LocalRequest localRequest = LocalContext.get();
                        if (localRequest != null && localRequest.getUserId() != null) {
                            merchantLogService.save(merchantInfoDO.getId(), MerchantEnum.MERCHANT_LOG_TYPE.MERCHANT, operType, remark);
                        }
                    }
                    break;
                default:
                    merchantFinanceRecordDO.setSysType(CommonEnum.SYS_TYPE.AGENT.getCode());
            }
            merchantFinanceRecordDO.setOrderNo(orderNo);
            merchantFinanceRecordDO.setType(financeRecordCommonReq.getType());
            merchantFinanceRecordDO.setChangeType(financeRecordCommonReq.getChangeType());
            merchantFinanceRecordService.addFinanceRecord(merchantFinanceRecordDO);
            return CommonResult.success();
        }
        catch (Exception e) {
            log.error("addFinanceRecord error:{}", e, e.getMessage());
            return CommonResult.error(CommonResultCode.SERVICE_ERROR);
        }
    }

    @Override
    public String rechargeCallBack(OpenApiResult<PayOrderResp> orderRespOpenApiResult) {
        log.info("rechargeCallBack orderRespOpenApiResult:{}", JsonUtils.toJsonString(orderRespOpenApiResult));
        try {
            PayOrderResp payOrderResp = orderRespOpenApiResult.getData();
            MerchantFinanceChargeDO merchantFinanceChargeDO = merchantFinanceChargeService.queryCharge(payOrderResp.getMchOrderNo());
            if (merchantFinanceChargeDO == null) {
                return "fail";
            }
            if (merchantFinanceChargeDO.getState().equals(payOrderResp.getState())) {
                return "success";
            }
            merchantFinanceChargeService.updateCharge(merchantFinanceChargeDO.getId(), payOrderResp.getState());
            PayOrderEnums.PAY_ORDER_STATUS payOrderStatus = PayOrderEnums.PAY_ORDER_STATUS.from(payOrderResp.getState());
            switch (payOrderStatus) {
                case SUCCESS:
                    // 充值流水增加
                    if (PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode().equals(payOrderResp.getState())) {
                        MerchantFinanceRecordReq merchantFinanceRecordReq = new MerchantFinanceRecordReq();
                        merchantFinanceRecordReq.setSn(merchantFinanceChargeDO.getSn());
                        merchantFinanceRecordReq.setAmount(MerchantUtils.bigDecimalToString(merchantFinanceChargeDO.getAmount()));
                        merchantFinanceRecordReq.setChangeType(CommonEnum.YES_NO_TYPE.YES.getCode());
                        merchantFinanceRecordReq.setType(FinanceEnum.FINANCE_TYPE.MERCHANT_FEE.getCode());
                        merchantFinanceRecordReq.setOrderNo(merchantFinanceChargeDO.getPayOrderNo());
                        addFinanceRecord(merchantFinanceRecordReq);
                    }
                    break;
            }
        }
        catch (Exception e) {
            log.error("rechargeCallBack error:{}", e, e.getMessage());
            return "fail";
        }
        return "success";
    }

    @Override
    public void returnFinanceRecord(MerchantFinanceRecordReturnReq merchantFinanceRecordReturnReq) {
        MerchantFinanceRecordPageQueryDto merchantFinanceRecordPageQueryDto = new MerchantFinanceRecordPageQueryDto();
        merchantFinanceRecordPageQueryDto.setOrderNo(merchantFinanceRecordReturnReq.getPayOrderNo());
        List<MerchantFinanceRecordDO> merchantFinanceRecordList = merchantFinanceRecordService.queryList(merchantFinanceRecordPageQueryDto);
        if (CollectionUtils.isEmpty(merchantFinanceRecordList)) {
            return;
        }
        merchantFinanceRecordList.stream().forEach(merchantFinanceRecordDO -> {
            MerchantFinanceRecordReq merchantFinanceRecordReq = new MerchantFinanceRecordReq();
            merchantFinanceRecordReq.setOrderNo(merchantFinanceRecordReturnReq.getRefundOrderNo());
            merchantFinanceRecordReq.setSn(merchantFinanceRecordDO.getSn());
            merchantFinanceRecordReq
                    .setAmount(MerchantUtils.bigDecimalToString(MerchantUtils.multiply(merchantFinanceRecordDO.getAmount(), merchantFinanceRecordReturnReq.getRate())));
            if (CommonEnum.YES_NO_TYPE.YES.getCode().equals(merchantFinanceRecordDO.getChangeType())) {
                merchantFinanceRecordReq.setChangeType(CommonEnum.YES_NO_TYPE.NO.getCode());
            }
            else {
                merchantFinanceRecordReq.setChangeType(CommonEnum.YES_NO_TYPE.YES.getCode());
            }
            merchantFinanceRecordReq.setType(merchantFinanceRecordDO.getType());
            merchantFinanceRecordReq.setRemark("交易订单退款");
            addFinanceRecord(merchantFinanceRecordReq);
        });
    }
}
